# This code is hosted on http://code.google.com/p/lenthorp/
# Freely available for use in applications, but should NOT be modified
# Email all comments to lenthorpresearch@gmail.com

import PricingEngineMod
from PricingEngineMod import *
reload(PricingEngineMod)

import BasicAsianMonteCarloEngine_QLMod
from BasicAsianMonteCarloEngine_QLMod import *
reload(BasicAsianMonteCarloEngine_QLMod)

import GPUAsianMonteCarloEngine_QLMod
from GPUAsianMonteCarloEngine_QLMod import *
reload(GPUAsianMonteCarloEngine_QLMod)

from datetime import date, timedelta

# modelParams: nsims, randomType, isGPU
# otherParams: vol, strike, tenor, rate, initial, div, fixingTimes, currentAverage, numPastFixings
class AsianBlackScholesMonteCarlo(PricingEngine):

    def preComputations(self):
        pass

    def getPrice(self):
        if (self.currentStatus > 0):
            op = self.otherParams.p
            mp = self.modelParams.p

            todaysDate = Date(15,May,1998)
            Settings.instance().evaluationDate = todaysDate
            settlementDate = Date(17,May,1998)
            initial = op['initial']
            strike = op['strike']
            rate = op['rate']
            vol = op['vol']
            div = op['div']
            tenor = op['tenor']
            currentAverage = op['currentAverage']
            numPastFixings = op['numPastFixings']
            futFixingDates = DateVector(len(op['fixingTimes']))
            count = 0
            for yearTime in op['fixingTimes']:
                tempDate = date(todaysDate.year(),todaysDate.month(),todaysDate.dayOfMonth()) + timedelta(days = yearTime * 365.25)
                futFixingDates[count] = Date(tempDate.day,tempDate.month,tempDate.year)
                count += 1
            tempDate = date(todaysDate.year(),todaysDate.month(),todaysDate.dayOfMonth()) + timedelta(days = tenor * 365.25)
            maturity = Date(tempDate.day,tempDate.month,tempDate.year)
            underlying = SimpleQuote(initial)
            volatility = BlackConstantVol(todaysDate, TARGET(), vol, Actual365Fixed())
            dividendYield = FlatForward(settlementDate, div, Actual365Fixed())
            riskFreeRate = FlatForward(settlementDate, rate, Actual365Fixed())
            process1 = BlackScholesMertonProcess(QuoteHandle(underlying),
                                     YieldTermStructureHandle(dividendYield),
                                     YieldTermStructureHandle(riskFreeRate),
                                     BlackVolTermStructureHandle(volatility))
            type = Option.Put if op['type'] == 'put' else Option.Call
            payoff = PlainVanillaPayoff(type,strike)
            europeanExercise = EuropeanExercise(maturity)
            option = DiscreteAveragingAsianOption(Average.Arithmetic, currentAverage, numPastFixings, futFixingDates, payoff, europeanExercise)
            p = dict(self.otherParams.p.items() + self.modelParams.p.items())

            if mp['isGPU']:
                rng = GPUMersenneTwisterCreatedOnDevice()
                process2 = GBMSingleAsianOptionGPUPrePathGenerator(YieldTermStructureHandle(riskFreeRate), rng, process1)
                self.mce = GPUAsianMonteCarloEngine_QL()
                self.mce.initialise(process = process2, requiredSamples = p['nsims'])
                option.setPricingEngine(castPricingEnginePtr(self.mce.get()))
            else:
                self.mce = BasicAsianMonteCarloEngine_QL()
                self.mce.initialise(randomType = p['randomType'], process = process1, requiredSamples = p['nsims'], maxSamples = p['nsims'])
                option.setPricingEngine(castPricingEnginePtr(self.mce.get()))
            return option.NPV()
            